<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SpringBoot统一功能解决方案 | 技术小馆</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            line-height: 1.8;
            color: #333;
            background-color: #f9fafb;
        }
        h1, h2, h3, h4 {
            font-family: 'Noto Serif SC', serif;
            font-weight: 600;
            color: #1f2937;
        }
        .hero {
            background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
        }
        .code-block {
            background-color: #2d3748;
            border-radius: 0.5rem;
            position: relative;
        }
        .code-copy {
            position: absolute;
            right: 1rem;
            top: 1rem;
            color: #a0aec0;
            cursor: pointer;
            transition: all 0.2s;
        }
        .code-copy:hover {
            color: #fff;
        }
        .feature-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
        }
        .solution-card {
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        }
        .solution-card:hover {
            transform: scale(1.02);
            box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
        }
        .footer-link:hover {
            color: #a5b4fc;
        }
        .drop-cap:first-letter {
            float: left;
            font-size: 4rem;
            line-height: 0.8;
            margin-right: 0.5rem;
            font-weight: bold;
            color: #6366f1;
        }
    </style>
</head>
<body class="antialiased">
    <!-- Hero Section -->
    <section class="hero text-white py-20 px-4 sm:px-6 lg:px-8">
        <div class="max-w-6xl mx-auto">
            <div class="flex flex-col lg:flex-row items-center">
                <div class="lg:w-1/2 mb-10 lg:mb-0">
                    <h1 class="text-4xl md:text-5xl font-bold mb-6 leading-tight">使用 SpringBoot 解决项目统一功能</h1>
                    <p class="text-xl mb-8 opacity-90">高效、优雅、易维护的企业级解决方案</p>
                    <div class="flex flex-wrap gap-4">
                        <span class="bg-white bg-opacity-20 px-4 py-2 rounded-full">#拦截器</span>
                        <span class="bg-white bg-opacity-20 px-4 py-2 rounded-full">#AOP</span>
                        <span class="bg-white bg-opacity-20 px-4 py-2 rounded-full">#全局异常</span>
                        <span class="bg-white bg-opacity-20 px-4 py-2 rounded-full">#最佳实践</span>
                    </div>
                </div>
                <div class="lg:w-1/2">
                    <div class="bg-white bg-opacity-10 p-6 rounded-xl backdrop-blur-sm border border-white border-opacity-20 shadow-xl">
                        <div class="mermaid">
                            graph TD
                                A[统一功能问题] --> B[日志记录]
                                A --> C[异常处理]
                                A --> D[权限校验]
                                A --> E[参数校验]
                                B --> F[拦截器]
                                C --> G[全局异常处理]
                                D --> H[AOP]
                                E --> I[参数校验器]
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <!-- Main Content -->
    <div class="max-w-4xl mx-auto px-4 sm:px-6 py-12 lg:py-16">
        <!-- Introduction -->
        <section class="mb-16">
            <div class="mb-8">
                <h2 class="text-2xl font-bold mb-4 text-gray-800">在企业级 Java 项目开发中，我们常常需要处理统一功能问题</h2>
                <img src="https://cdn.nlark.com/yuque/0/2025/png/21449790/1740819270195-7f26e3f1-aed9-440b-8b6f-c5330cae63e5.png" alt="SpringBoot统一功能" class="w-full rounded-lg shadow-md mb-6">
                <p class="text-lg leading-relaxed drop-cap">在企业级 Java 项目开发中，我们常常需要处理 <strong class="text-indigo-600">日志记录、异常处理、权限校验、请求参数校验等通用功能</strong>。如果每个模块都单独实现这些功能，不仅代码冗余，还会增加维护成本。</p>
                <p class="text-lg leading-relaxed mt-4">Spring 框架提供了一系列 <strong class="text-indigo-600">非侵入式</strong> 的解决方案，如 <strong class="text-indigo-600">拦截器（Interceptor）、AOP（Aspect-Oriented Programming）、全局异常处理</strong> 等，让我们能够 <strong class="text-indigo-600">优雅地</strong> 解决这些统一功能问题。</p>
            </div>
        </section>

        <!-- Why we need unified solutions -->
        <section class="mb-16">
            <h2 class="text-3xl font-bold mb-6 flex items-center">
                <span class="bg-indigo-100 text-indigo-700 w-10 h-10 rounded-full flex items-center justify-center mr-4">1</span>
                <span>为什么需要统一功能？</span>
            </h2>
            
            <div class="grid md:grid-cols-2 gap-8 mb-8">
                <div class="bg-white p-6 rounded-xl shadow-md solution-card">
                    <div class="flex items-center mb-4">
                        <div class="bg-indigo-100 text-indigo-600 p-3 rounded-full mr-4">
                            <i class="fas fa-file-alt text-xl"></i>
                        </div>
                        <h3 class="text-xl font-semibold">日志记录问题</h3>
                    </div>
                    <p>每个请求都需要打印日志，如果在 Controller 里手写 <code class="bg-gray-100 px-1 rounded">System.out.println()</code>，代码会非常冗余。</p>
                </div>
                
                <div class="bg-white p-6 rounded-xl shadow-md solution-card">
                    <div class="flex items-center mb-4">
                        <div class="bg-red-100 text-red-600 p-3 rounded-full mr-4">
                            <i class="fas fa-bug text-xl"></i>
                        </div>
                        <h3 class="text-xl font-semibold">异常处理问题</h3>
                    </div>
                    <p>如果每个方法都 <code class="bg-gray-100 px-1 rounded">try-catch</code>，代码不仅繁琐，还降低了可读性。</p>
                </div>
                
                <div class="bg-white p-6 rounded-xl shadow-md solution-card">
                    <div class="flex items-center mb-4">
                        <div class="bg-yellow-100 text-yellow-600 p-3 rounded-full mr-4">
                            <i class="fas fa-user-shield text-xl"></i>
                        </div>
                        <h3 class="text-xl font-semibold">权限校验问题</h3>
                    </div>
                    <p>有些 API 需要权限控制，如果每个接口都手动检查权限，维护起来非常麻烦。</p>
                </div>
                
                <div class="bg-white p-6 rounded-xl shadow-md solution-card">
                    <div class="flex items-center mb-4">
                        <div class="bg-green-100 text-green-600 p-3 rounded-full mr-4">
                            <i class="fas fa-check-circle text-xl"></i>
                        </div>
                        <h3 class="text-xl font-semibold">请求参数校验</h3>
                    </div>
                    <p>如果每个 Controller 里都手写 <code class="bg-gray-100 px-1 rounded">if</code> 语句检查参数合法性，代码可读性会很差。</p>
                </div>
            </div>
            
            <div class="bg-blue-50 border-l-4 border-blue-500 p-4 rounded-r">
                <p class="text-blue-800"><strong>核心观点：</strong> 这些功能都是 <strong>通用功能</strong>，不应该和业务逻辑代码混在一起。因此，我们需要 <strong>Spring 提供的机制</strong>，来解决这些问题。</p>
            </div>
        </section>

        <!-- SpringBoot Solutions -->
        <section class="mb-16">
            <h2 class="text-3xl font-bold mb-6 flex items-center">
                <span class="bg-indigo-100 text-indigo-700 w-10 h-10 rounded-full flex items-center justify-center mr-4">2</span>
                <span>解决统一功能的 SpringBoot 方案</span>
            </h2>
            
            <img src="https://cdn.nlark.com/yuque/0/2025/png/21449790/1740819015410-b8a1d1a8-fbdf-4e0b-98a4-bfacbf3373a8.png" alt="Spring解决方案" class="w-full rounded-lg shadow-md mb-8">
            
            <div class="grid md:grid-cols-3 gap-6 mb-8">
                <div class="bg-white p-6 rounded-xl shadow-lg border-t-4 border-indigo-500 feature-card">
                    <div class="text-indigo-600 text-3xl mb-4">
                        <i class="fas fa-filter"></i>
                    </div>
                    <h3 class="text-xl font-bold mb-3">拦截器(Interceptor)</h3>
                    <p class="text-gray-600">适用于 <strong>全局请求拦截</strong>，比如日志记录、权限校验等。</p>
                </div>
                
                <div class="bg-white p-6 rounded-xl shadow-lg border-t-4 border-purple-500 feature-card">
                    <div class="text-purple-600 text-3xl mb-4">
                        <i class="fas fa-project-diagram"></i>
                    </div>
                    <h3 class="text-xl font-bold mb-3">AOP(切面编程)</h3>
                    <p class="text-gray-600">适用于 <strong>方法级别的拦截</strong>，如事务管理、性能监控等。</p>
                </div>
                
                <div class="bg-white p-6 rounded-xl shadow-lg border-t-4 border-blue-500 feature-card">
                    <div class="text-blue-600 text-3xl mb-4">
                        <i class="fas fa-exclamation-triangle"></i>
                    </div>
                    <h3 class="text-xl font-bold mb-3">全局异常处理</h3>
                    <p class="text-gray-600">适用于 <strong>统一异常管理</strong>，避免代码中充斥 try-catch 语句。</p>
                </div>
            </div>
            
            <p class="text-lg mb-8">我们通过 <strong class="text-indigo-600">两个实战案例</strong> 详细讲解如何使用这些技术。</p>
        </section>

        <!-- Case 1: Logging Interceptor -->
        <section class="mb-16">
            <h2 class="text-3xl font-bold mb-6 flex items-center">
                <span class="bg-indigo-100 text-indigo-700 w-10 h-10 rounded-full flex items-center justify-center mr-4">3</span>
                <span>实战案例 1：使用拦截器实现统一日志管理</span>
            </h2>
            
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8">
                <div class="bg-gray-800 text-white px-6 py-3 flex justify-between items-center">
                    <h3 class="font-mono text-sm">需求分析</h3>
                    <div class="flex space-x-2">
                        <span class="w-3 h-3 bg-red-500 rounded-full"></span>
                        <span class="w-3 h-3 bg-yellow-500 rounded-full"></span>
                        <span class="w-3 h-3 bg-green-500 rounded-full"></span>
                    </div>
                </div>
                <div class="p-6">
                    <p class="mb-6">我们希望 <strong>记录所有 API 请求的日志</strong>，包括 <strong>请求时间、请求路径、请求参数、请求耗时</strong> 等。</p>
                    
                    <div class="bg-gray-100 p-4 rounded-lg mb-6">
                        <h4 class="font-bold text-gray-700 mb-2 flex items-center">
                            <i class="fas fa-lightbulb text-yellow-500 mr-2"></i>
                            解决方案
                        </h4>
                        <p>Spring 提供了 <code class="bg-gray-200 px-1 rounded">HandlerInterceptor</code> 接口，我们可以自定义一个拦截器，在 <strong>请求开始前、请求结束后</strong> 记录日志。</p>
                    </div>
                    
                    <h3 class="text-xl font-bold mb-4 mt-8 flex items-center">
                        <i class="fas fa-code text-indigo-500 mr-2"></i>
                        实现步骤
                    </h3>
                    
                    <h4 class="font-bold text-gray-700 mb-2">1. 创建日志拦截器</h4>
                    <div class="code-block mb-6">
                        <button class="code-copy" title="复制代码">
                            <i class="far fa-copy"></i>
                        </button>
                        <pre class="text-gray-200 p-4 overflow-x-auto"><code>import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component
public class LogInterceptor implements HandlerInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);

    private static final ThreadLocal&lt;Long&gt; startTime = new ThreadLocal&lt;&gt;();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        startTime.set(System.currentTimeMillis());
        logger.info("请求路径: {}，请求参数: {}", request.getRequestURI(), request.getQueryString());
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        long duration = System.currentTimeMillis() - startTime.get();
        logger.info("请求结束: {}，耗时: {}ms", request.getRequestURI(), duration);
        startTime.remove();
    }
}</code></pre>
                    </div>
                    
                    <h4 class="font-bold text-gray-700 mb-2">2. 注册拦截器</h4>
                    <div class="code-block mb-6">
                        <button class="code-copy" title="复制代码">
                            <i class="far fa-copy"></i>
                        </button>
                        <pre class="text-gray-200 p-4 overflow-x-auto"><code>import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LogInterceptor logInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logInterceptor).addPathPatterns("/**");
    }
}</code></pre>
                    </div>
                    
                    <div class="bg-green-50 border-l-4 border-green-500 p-4 rounded-r">
                        <p class="text-green-800"><strong>效果：</strong> 所有请求的日志都会自动记录，业务代码完全解耦！</p>
                    </div>
                </div>
            </div>
        </section>

        <!-- Case 2: AOP for Auth -->
        <section class="mb-16">
            <h2 class="text-3xl font-bold mb-6 flex items-center">
                <span class="bg-indigo-100 text-indigo-700 w-10 h-10 rounded-full flex items-center justify-center mr-4">4</span>
                <span>实战案例 2：使用 AOP 实现统一权限管理</span>
            </h2>
            
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8">
                <div class="bg-gray-800 text-white px-6 py-3 flex justify-between items-center">
                    <h3 class="font-mono text-sm">需求分析</h3>
                    <div class="flex space-x-2">
                        <span class="w-3 h-3 bg-red-500 rounded-full"></span>
                        <span class="w-3 h-3 bg-yellow-500 rounded-full"></span>
                        <span class="w-3 h-3 bg-green-500 rounded-full"></span>
                    </div>
                </div>
                <div class="p-6">
                    <p class="mb-6">假设我们的系统有 <strong>管理员和普通用户</strong>，我们希望 <strong>某些 API 只有管理员能访问</strong>。</p>
                    
                    <div class="bg-gray-100 p-4 rounded-lg mb-6">
                        <h4 class="font-bold text-gray-700 mb-2 flex items-center">
                            <i class="fas fa-lightbulb text-yellow-500 mr-2"></i>
                            解决方案
                        </h4>
                        <p>使用 <strong>Spring AOP</strong>，在方法执行前检查用户角色，如果权限不足，直接返回错误信息。</p>
                    </div>
                    
                    <h3 class="text-xl font-bold mb-4 mt-8 flex items-center">
                        <i class="fas fa-code text-indigo-500 mr-2"></i>
                        实现步骤
                    </h3>
                    
                    <h4 class="font-bold text-gray-700 mb-2">1. 自定义权限注解</h4>
                    <div class="code-block mb-6">
                        <button class="code-copy" title="复制代码">
                            <i class="far fa-copy"></i>
                        </button>
                        <pre class="text-gray-200 p-4 overflow-x-auto"><code>import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminOnly {
}</code></pre>
                    </div>
                    
                    <h4 class="font-bold text-gray-700 mb-2">2. 创建权限校验切面</h4>
                    <div class="code-block mb-6">
                        <button class="code-copy" title="复制代码">
                            <i class="far fa-copy"></i>
                        </button>
                        <pre class="text-gray-200 p-4 overflow-x-auto"><code>import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AuthAspect {
    @Before("@annotation(AdminOnly)")
    public void checkAdmin() {
        String currentUserRole = "USER"; // 模拟获取当前用户角色
        if (!"ADMIN".equals(currentUserRole)) {
            throw new RuntimeException("权限不足，只有管理员可以访问！");
        }
    }
}</code></pre>
                    </div>
                    
                    <h4 class="font-bold text-gray-700 mb-2">3. 在 API 上使用注解</h4>
                    <div class="code-block mb-6">
                        <button class="code-copy" title="复制代码">
                            <i class="far fa-copy"></i>
                        </button>
                        <pre class="text-gray-200 p-4 overflow-x-auto"><code>import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin")
public class AdminController {
    @GetMapping("/dashboard")
    @AdminOnly
    public String adminDashboard() {
        return "欢迎来到管理员面板！";
    }
}</code></pre>
                    </div>
                    
                    <div class="bg-green-50 border-l-4 border-green-500 p-4 rounded-r">
                        <p class="text-green-800"><strong>效果：</strong> 权限校验被抽离到 AOP 逻辑，业务代码更清晰、更易维护！</p>
                    </div>
                </div>
            </div>
        </section>

        <!-- Global Exception Handling -->
        <section class="mb-16">
            <h2 class="text-3xl font-bold mb-6 flex items-center">
                <span class="bg-indigo-100 text-indigo-700 w-10 h-10 rounded-full flex items-center justify-center mr-4">5</span>
                <span>全局异常处理</span>
            </h2>
            
            <div class="bg-white p-6 rounded-xl shadow-md">
                <p class="mb-6">在项目中，我们希望 <strong>所有异常都有统一的返回格式</strong>，而不是让 <code class="bg-gray-100 px-1 rounded">try-catch</code> 充斥代码。Spring 提供了 <code class="bg-gray-100 px-1 rounded">@ControllerAdvice</code> 让我们 <strong>统一管理异常</strong>。</p>
                
                <h3 class="text-xl font-bold mb-4 flex items-center">
                    <i class="fas fa-code text-indigo-500 mr-2"></i>
                    实现代码
                </h3>
                
                <div class="code-block mb-6">
                    <button class="code-copy" title="复制代码">
                        <i class="far fa-copy"></i>
                    </button>
                    <pre class="text-gray-200 p-4 overflow-x-auto"><code>import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e) {
        return "服务器错误：" + e.getMessage();
    }
}</code></pre>
                </div>
                
                <div class="bg-green-50 border-l-4 border-green-500 p-4 rounded-r">
                    <p class="text-green-800"><strong>效果：</strong> 所有异常都会被统一捕获，避免业务代码中充斥 try-catch！</p>
                </div>
            </div>
        </section>

        <!-- Summary -->
        <section class="mb-16">
            <h2 class="text-3xl font-bold mb-6 flex items-center">
                <span class="bg-indigo-100 text-indigo-700 w-10 h-10 rounded-full flex items-center justify-center mr-4">6</span>
                <span>总结</span>
            </h2>
            
            <div class="bg-indigo-50 p-6 rounded-xl">
                <div class="grid md:grid-cols-3 gap-6">
                    <div class="bg-white p-4 rounded-lg shadow-sm">
                        <h3 class="font-bold text-indigo-600 mb-2">拦截器(Interceptor)</h3>
                        <p class="text-gray-600">适用于 <strong>全局请求拦截</strong>，如日志记录、请求统计、统一参数处理。</p>
                    </div>
                    
                    <div class="bg-white p-4 rounded-lg shadow-sm">
                        <h3 class="font-bold text-indigo-600 mb-2">AOP(切面编程)</h3>
                        <p class="text-gray-600">适用于 <strong>方法级别的功能增强</strong>，如权限校验、事务管理、性能监控。</p>
                    </div>
                    
                    <div class="bg-white p-4 rounded-lg shadow-sm">
                        <h3 class="font-bold text-indigo-600 mb-2">全局异常处理</h3>
                        <p class="text-gray-600">可用于 <strong>集中管理异常</strong>，避免 <code class="bg-gray-100 px-1 rounded">try-catch</code> 代码污染业务逻辑。</p>
                    </div>
                </div>
            </div>
        </section>
    </div>

    <!-- Footer -->
    <footer class="bg-gray-900 text-gray-300 py-8">
        <div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
            <div class="flex flex-col md:flex-row justify-between items-center">
                <div class="mb-4 md:mb-0">
                    <h3 class="text-white text-lg font-semibold">技术小馆</h3>
                    <p class="text-gray-400">专业的Java技术分享平台</p>
                </div>
                <div>
                    <a href="http://www.yuque.com/jtostring" class="footer-link hover:text-white transition-colors duration-200">
                        <i class="fas fa-globe mr-2"></i>
                        http://www.yuque.com/jtostring
                    </a>
                </div>
            </div>
            <div class="mt-8 pt-8 border-t border-gray-800 text-center text-sm text-gray-400">
                <p>© 2023 技术小馆. 保留所有权利.</p>
            </div>
        </div>
    </footer>

    <script>
        // Initialize Mermaid
        mermaid.initialize({
            startOnLoad: true,
            theme: 'dark',
            flowchart: {
                useMaxWidth: false,
                htmlLabels: true,
                curve: 'basis'
            }
        });

        // Add copy functionality to code blocks
        document.querySelectorAll('.code-copy').forEach(button => {
            button.addEventListener('click', () => {
                const codeBlock = button.parentNode.querySelector('code');
                const range = document.createRange();
                range.selectNode(codeBlock);
                window.getSelection().removeAllRanges();
                window.getSelection().addRange(range);
                document.execCommand('copy');
                window.getSelection().removeAllRanges();
                
                const originalIcon = button.innerHTML;
                button.innerHTML = '<i class="fas fa-check"></i>';
                setTimeout(() => {
                    button.innerHTML = originalIcon;
                }, 2000);
            });
        });
    </script>
</body>
</html>